package pl.enigmatic.parser.xml;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections.map.MultiKeyMap;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import pl.enigmatic.util.Reflection;
import pl.enigmatic.parser.metadata.Metadata;
import pl.enigmatic.parser.string.Converter;


@SuppressWarnings("rawtypes")
public final class MapParser extends Parser<Map> {

	/** a collection of parsers maintained to keep the ones already created */
	private static final MultiKeyMap parsers = new MultiKeyMap();

	protected static MapParser createMapParser(final Class<?> key, final Class<?> value) {
		if (!parsers.containsKey(key.getName(), value.getName())) {
			final MapParser parser = new MapParser(key, value);
			parsers.put(key.getName(), value.getName(), parser);
			return parser;
		}
		return (MapParser) parsers.get(key.getName(), value.getName());
	}

	protected static MapParser createMapParser(final String key, final String value) {
		if (!parsers.containsKey(key, value)) {
			return createMapParser(Reflection.classForName(key), Reflection.classForName(value));
		} else {
			return (MapParser) parsers.get(key, value);
		}
	}

	/** value parser */
	private final Parser<?> parser;
	/** key converter */
	private final Converter<?> converter;

	private MapParser(final Parser<?> parser, final Converter<?> converter) {
		super(Map.class, false);
		this.converter = converter;
		this.parser = parser;
	}

	MapParser(final Class<?> key, final Class<?> value) {
		this(createParser(value), Converter.createDefaultConverter(key));
	}

	public final MapParser copy(final Parser<?> custom) {
		if (custom == parser || custom == null) {
			return this;
		} else {
			return new MapParser(custom, converter);
		}
	}

	@Override
	protected boolean parseAttribute(final Map target, final Node item, final ParsingInfo<Map> info) {
		return false;
	}

	@Override
	protected Map newInstance(final ParsingInfo<Map> info) {
		return new HashMap();
	}

	@SuppressWarnings("unchecked")
	@Override
	protected boolean parseChildElement(final Map map, final Element element, final ParsingInfo<Map> info) {
		final Object key = converter.convert(Metadata.KEY.getString(element));
		Object value = null;
		final String clazz = NodeNameInfo.getFullClassName(element, info.getStaticInfo());
		final Parser parser = clazz == null ? this.parser : Parser.createParser(clazz);
		value = parser.parse(info.createChild(parser, null, element));
		map.put(key, value);
		return true;
	}
}
